package xyz.xtt.pay.service.impl;

import static xyz.xtt.pay.utils.AssertUtils.isFalse;
import static xyz.xtt.pay.utils.AssertUtils.isNull;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.google.common.primitives.Longs;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.client.request.CancelTransactionRequest;
import xyz.xtt.asset.client.request.ConfirmTransactionRequest;
import xyz.xtt.asset.client.request.MakeTransactionRequest;
import xyz.xtt.asset.client.response.AssetInfoResponse;
import xyz.xtt.asset.client.response.AssetTransactionResponse;
import xyz.xtt.asset.client.response.TransactionResponse;
import xyz.xtt.pay.api.enums.PayOrderBusinessTypeEnum;
import xyz.xtt.pay.api.enums.PayOrderStatusEnum;
import xyz.xtt.pay.constants.ErrorMessage;
import xyz.xtt.pay.dto.pay.GoodsInfo;
import xyz.xtt.pay.entity.TblPayOrder;
import xyz.xtt.pay.entity.TblPayOrderLog;
import xyz.xtt.pay.enums.DealStateEnum;
import xyz.xtt.pay.exception.ChainException;
import xyz.xtt.pay.service.IInnerService;
import xyz.xtt.pay.service.INftOrderService;
import xyz.xtt.pay.service.IPayOrderOperator;
import xyz.xtt.pay.service.IRocketMqService;
import xyz.xtt.pay.service.ITblPayOrderLogService;
import xyz.xtt.pay.service.ITblPayOrderService;
import xyz.xtt.pay.support.PayCenterHttpService;
import xyz.xtt.pay.utils.RedisLockUtil;
import xyz.xtt.pay.vo.CreateOrderRequest;
import xyz.xtt.pay.vo.PayCallbackMessage;
import xyz.xtt.pay.vo.PayCenterArgsResponse;
import xyz.xtt.user.client.response.UserInfoResponse;

/**
 * @author dale
 * @date 2025/2/10
 **/
@Component
@Slf4j
public class PersonalMintPayOrderOperator implements IPayOrderOperator {
	@Autowired
	private PayCenterHttpService payCenterHttpService;
	@Autowired
	private RedisLockUtil redisLockUtil;
	@Autowired
	private ITblPayOrderService iTblPayOrderService;
	@Autowired
	private INftOrderService nftOrderService;
	@Autowired
	private IRocketMqService rocketMqService;
	@Autowired
	private ITblPayOrderLogService iTblPayOrderLogService;

	@Autowired
	private IInnerService iInnerService;

	@Value("${personal.mint.order.price}")
	private String personalMintPrice;

	@Override
	public boolean canOperateType(PayOrderBusinessTypeEnum type) {
		return PayOrderBusinessTypeEnum.PERSONAL_MINT.equals(type);
	}

	@Override
	public PayCenterArgsResponse createPayOrder(UserInfoResponse tblUser, PayOrderBusinessTypeEnum orderTypeEnum, CreateOrderRequest request,
			GoodsInfo goodsInfo) {
		AssetInfoResponse asset = iInnerService.infoWithAssetId(Long.valueOf(request.getAssetId()));
		if (Objects.isNull(asset)) {
			throw new ChainException(ErrorMessage.ASSET_NOT_EXIST);
		} else if (asset.getAmount() < request.getPurchaseAmount()) {
			log.info("asset amount:{} less than purchase target:{}", asset.getAmount(), request.getPurchaseAmount());
			throw new ChainException(ErrorMessage.STOCK_NOT_ENOUGH);
		} else if (Objects.equals(asset.getOwnerType(), 0) && Objects.equals(orderTypeEnum, PayOrderBusinessTypeEnum.NORMAL)) {
			throw new ChainException(ErrorMessage.ASSET_STATUS_EX);
		} else if (Objects.equals(PayOrderBusinessTypeEnum.PERSONAL_MINT, orderTypeEnum) && !Objects.equals(asset.getAuthorId(), tblUser.getAppUserId())) {
			log.info("personal mint asset owner:{} not equal to uid:{}", asset.getAuthorId(), tblUser.getAppUserId());
			throw new ChainException(ErrorMessage.ASSET_NOT_EXIST);
		} else if (!Objects.equals(asset.getSellPattern(), CAN_SELL_PATTERN) && Objects.equals(orderTypeEnum, PayOrderBusinessTypeEnum.NORMAL)) {
			log.error("asset sellPattern not support,asset:{}", JSONUtil.toJsonStr(asset));
			throw new ChainException(ErrorMessage.ASSET_STATUS_EX);
		}

		// 分布式锁同步（商品+用户）
		String lockKey = String.format(RedisLockUtil.LOCK_KEY_USER_PURCHASE, tblUser.getAppUserId(), request.getAssetId());
		String lockValue = UUID.randomUUID().toString();
		boolean lock = false;
		try {
			lock = redisLockUtil.acquireLock(lockKey, lockValue, 1, TimeUnit.MINUTES);
			if (lock) {
				// 检查当前资产当前用户是否存在未支付完成订单，如果存在则返回现有订单
				TransactionResponse notPayAssetTransaction = iInnerService.queryNotPayOrder(asset.getId(), String.valueOf(tblUser.getAppUserId()));
				if (Objects.nonNull(notPayAssetTransaction)) {
					log.info("createPayOrder uid:{} notPayAssetTransaction:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(notPayAssetTransaction));
					TblPayOrder payOrder = iTblPayOrderService.getByBusiness(orderTypeEnum, String.valueOf(notPayAssetTransaction.getOrderId()));
					isNull(payOrder, ErrorMessage.ORDER_CREATE_FAILED);
					log.info("createPayOrder uid:{} current payOrder:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(payOrder));
					PayCenterArgsResponse response = payCenterHttpService.buildCreateOrderParams(payOrder, tblUser, request.getBackUrl(), asset);
					response.setCreateNewOrder(false);
					return response;
				}

				// 创建资产交易
				log.info("createPayOrder start create asset transaction uid:{} request:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(request));
				MakeTransactionRequest makeTransactionRequest = MakeTransactionRequest
						.builder()
						.from(Long.valueOf(asset.getAuthorId()))
						.to(tblUser.getAppUserId())
						.num(request.getPurchaseAmount())
						.assetId(asset.getId())
						.price(asset.getPrice().toPlainString())
						.expireTimeWithLevel(0)
						.orderType(orderTypeEnum.name())
						.fees("0")
						.build();
				BigDecimal totalPrice;
				if (Objects.equals(PayOrderBusinessTypeEnum.PERSONAL_MINT, orderTypeEnum)) {
					totalPrice = new BigDecimal(personalMintPrice);
					makeTransactionRequest.setFees(personalMintPrice);
					makeTransactionRequest.setNum(asset.getAmount());
				} else {
					totalPrice = asset.getPrice().multiply(new BigDecimal(request.getPurchaseAmount()));
				}
				makeTransactionRequest.setOrderPrice(totalPrice);
				makeTransactionRequest.setBizType("nft");
				AssetTransactionResponse transaction = iInnerService.makeTransaction(makeTransactionRequest);
				isNull(transaction, ErrorMessage.ORDER_CREATE_FAILED, () -> log
						.info("uid:{} makeOrderRequest:{} transaction result is null", tblUser.getAppUserId(), JSONUtil.toJsonStr(makeTransactionRequest)));

				// 创建支付订单
				BigDecimal actualPayMoney;
				if (Objects.equals(PayOrderBusinessTypeEnum.PERSONAL_MINT, orderTypeEnum)) {
					actualPayMoney = new BigDecimal(personalMintPrice);
				} else {
					actualPayMoney = asset.getPrice().multiply(new BigDecimal(request.getPurchaseAmount()));
				}
				TblPayOrder payOrder = nftOrderService
						.saveUnPayOrder(transaction, orderTypeEnum, String.valueOf(transaction.getOrderId()), actualPayMoney, asset);

				// 插入延时取消任务
				nftOrderService.sendPayDelayMessage(payOrder);
				return payCenterHttpService.buildCreateOrderParams(payOrder, tblUser, request.getBackUrl(), asset);
			} else {
				log.warn("createOrder failed. acquire lock on goods and user failed. uid:{} param:{}", tblUser.getAppUserId(), JSONUtil.toJsonStr(request));
			}
		} finally {
			if (lock) {
				redisLockUtil.releaseLock(lockKey, lockValue);
			}
		}
		throw new ChainException(ErrorMessage.UNKNOWN_ERROR);
	}

	/**
	 * 确认支付订单
	 *
	 * @param payOrder
	 * @param callbackMessage
	 * @param appUserId
	 */
	@Override
	public boolean confirmPayOrder(TblPayOrder payOrder, PayCallbackMessage callbackMessage, String appUserId) {
		boolean needRefund = false;
		// 业务订单确认
		TransactionResponse transaction = iInnerService.queryPaySyncOne(appUserId, Longs.tryParse(payOrder.getBusinessRefId()));
		isNull(transaction, ErrorMessage.ORDER_OPERATE_FAILED, () -> log.error("支付中心支付成功，没有找到对应的业务订单 {}", JSONUtil.toJsonStr(callbackMessage)));
		if (Objects.equals(transaction.getDealState(), DealStateEnum.NOT_PAYED.code())) {
			ConfirmTransactionRequest confirmOrderRequest = ConfirmTransactionRequest
					.builder()
					.orderId(transaction.getOrderId())
					.from(Long.valueOf(transaction.getSellerUserId()))
					.to(Long.valueOf(transaction.getBuyerUserId()))
					.assetId(transaction.getAssetId())
					.actTotalPrice(payOrder.getPayMoney())
					.build();
			try {
				// 记录支付成功
				nftOrderService.savePaySuccessRecord(payOrder, callbackMessage);
				iInnerService.confirmTransaction(confirmOrderRequest);
				// 资产增加上链标识
				if (Objects.equals(payOrder.getBusinessType(), PayOrderBusinessTypeEnum.PERSONAL_MINT.getCode())) {
					nftOrderService.saveUpChainFlag(payOrder);
				}
				// 发送支付成功消息
				rocketMqService.sendPayOrderStatusChangeMsg(payOrder.getPayOrderId());
				return true;
			} catch (ChainException e) {
				log.error("支付中心支付成功，业务订单确认失败 {}", JSONUtil.toJsonStr(callbackMessage), e);
				// 处理失败，取消订单
				doPayCancel(payOrder, "支付中心支付成功，业务订单确认失败. exp:" + e.getMsg() + " callbackMessage:" + JSONUtil.toJsonStr(callbackMessage));
				needRefund = true;
			}
		} else if (Objects.equals(payOrder.getPayStatus(), PayOrderStatusEnum.PAY_CANCEL.getCode())) {
			if (nftOrderService.alreadyApplyRefund(payOrder.getPayOrderId())) {
				log.warn("支付中心支付成功，业务订单已取消，已申请退款 {}", JSONUtil.toJsonStr(callbackMessage));
			} else {
				// 业务订单已取消，向支付中心申请退款
				needRefund = true;
			}
		} else {
			log.warn("支付处理失败，目标订单不是待支付状态！ {}", JSONUtil.toJsonStr(callbackMessage));
		}

		if (needRefund) {
			nftOrderService.startRefundOrder(payOrder, callbackMessage);
		}
		return false;
	}

	@Override
	public void doPayCancel(TblPayOrder payOrder, String remark) {
		// 取消关联业务交易
		if (Objects.equals(payOrder.getBusinessType(), PayOrderBusinessTypeEnum.NORMAL.getCode())
				|| Objects.equals(payOrder.getBusinessType(), PayOrderBusinessTypeEnum.PERSONAL_MINT.getCode())) {
			TransactionResponse transaction = iInnerService
					.queryPaySyncOne(String.valueOf(payOrder.getAppUserId()), Longs.tryParse(payOrder.getBusinessRefId()));
			CancelTransactionRequest cancelTransactionRequest = CancelTransactionRequest
					.builder()
					.orderId(Long.valueOf(payOrder.getBusinessRefId()))
					.from(Long.valueOf(transaction.getSellerUserId()))
					.to(Long.valueOf(transaction.getBuyerUserId()))
					.assetId(transaction.getAssetId())
					.build();
			iInnerService.cancelTransaction(cancelTransactionRequest);
		}

		// 取消payOrder
		TblPayOrderLog payOrderLog = TblPayOrderLog
				.builder()
				.payOrderLogId(IdUtil.getSnowflakeNextId())
				.payStatus(PayOrderStatusEnum.PAY_CANCEL.getCode())
				.payOrderId(payOrder.getPayOrderId())
				.createTime(System.currentTimeMillis())
				.detail(remark)
				.build();
		isFalse(iTblPayOrderLogService.save(payOrderLog), ErrorMessage.ORDER_OPERATE_FAILED);

		isFalse(iTblPayOrderService
				.updatePayStatus(payOrder.getPayOrderId(), PayOrderStatusEnum.UN_PAY, PayOrderStatusEnum.PAY_CANCEL), ErrorMessage.ORDER_OPERATE_FAILED);
		// 发送支付订单状态变更消息
		rocketMqService.sendPayOrderStatusChangeMsg(payOrder.getPayOrderId());
	}

	@Override
	public GoodsInfo getGoodsInfo(TblPayOrder payOrder) {
		return null;
	}
}
